import { ipcRenderer, webUtils } from "electron";
import type { IpcRendererInvoker, RendererDialog } from "@common/ipc-types.ts";

export const ipcInvoker = ((): IpcRendererInvoker => {
  let eventId = 0;
  const listeners = new Map<number, (...args: any) => any>();

  return {
    on(event: string, listener: (...args: any[]) => any) {
      // 同一个函数多次从前端传过来，在地址上并不是同一个函数，会导致事件解绑时失败
      // 因此需要将每个事件回调保存起来，并分配ID，通过事件ID来解绑
      eventId++;
      const _listener = (_: Electron.IpcRendererEvent, ...args: any) => listener(...args);
      listeners.set(eventId, _listener);
      ipcRenderer.on(event, _listener);
      return eventId;
    },
    off(channel: string, eventId?: number) {
      if (eventId != null) {
        const listener = listeners.get(eventId);
        if (listener) {
          ipcRenderer.off(channel, listener);
        }
        listeners.delete(eventId);
      } else {
        ipcRenderer.removeAllListeners(channel);
      }
    },

    send(...args: Parameters<typeof ipcRenderer.send>) {
      return ipcRenderer.send(...args);
    },

    invoke(...args: Parameters<typeof ipcRenderer.invoke>) {
      return ipcRenderer.invoke(...args);
    },

    invokeNode<T = any>(api: string, ...args: any[]): Promise<T> {
      return ipcRenderer.invoke("__invoke_node__", api, ...args);
    },

    invokeNodeChain<T = any>(args: ([string, any[] | undefined] | [string])[]): Promise<T> {
      return ipcRenderer.invoke("__invoke_node_chain__", args);
    },

    eventNames() {
      return ipcRenderer.eventNames();
    },
    listenerCount(eventName: string) {
      return ipcRenderer.listenerCount(eventName);
    },
  };
})();

export const webUtilsInvoker: Electron.WebUtils = {
  getPathForFile(file: File): string {
    return webUtils.getPathForFile(file);
  },
};

export const dialogInvoker: RendererDialog = {
  showOpenDialog(options: Electron.OpenDialogOptions): Promise<Electron.OpenDialogReturnValue> {
    return ipcInvoker.invoke("dialog.show-open", options);
  },
  showOpenDirectoryDialog(options?: Partial<Electron.OpenDialogOptions>): Promise<Electron.OpenDialogReturnValue> {
    return ipcInvoker.invoke("dialog.show-open", {
      title: "选择目录",
      ...options,
      properties: ["openDirectory"],
    } as Electron.OpenDialogOptions);
  },
  showSaveDialog(options: Electron.SaveDialogOptions): Promise<Electron.SaveDialogReturnValue> {
    return ipcInvoker.invoke("dialog.show-save", options);
  },
  showMessageBox(options: Electron.MessageBoxOptions): Promise<Electron.MessageBoxReturnValue> {
    return ipcInvoker.invoke("dialog.show-message", options);
  },
  showErrorBox(title: string, content: string) {
    ipcInvoker.send("dialog.show-error", title, content);
  },
};
